#include "DXUT.h"
#include "Updatequeue.h"


inline void Updatequeue::enqueue(int x, int z, int y, byte type, UINT64 time) {
	INTVECTOR4 vec = INTVECTOR4(x, z, y, type);
	QueElement elem = {time, vec};
	AcquireSRWLockShared(&m_Lock);
	if (m_Map.count(vec) == 0) {
		m_Queue.push(elem);
		m_Map.insert(vec);
	};
	ReleaseSRWLockShared(&m_Lock);
}

inline void Updatequeue::enqueue(QueElement elem) {
	INTVECTOR4 vec = elem.Position;//{elem.Position.x, elem.Position.z, elem.Position.y, elem.Position.w};
	AcquireSRWLockShared(&m_Lock);
	if (m_Map.count(vec) == 0) {
		m_Queue.push(elem);
		m_Map.insert(vec);
	};
	ReleaseSRWLockShared(&m_Lock);
}

inline QueElement Updatequeue::dequeue() {
	AcquireSRWLockShared(&m_Lock);
	QueElement elem = m_Queue.top();
	INTVECTOR4 vec  = elem.Position;//{elem.Position.x, elem.Position.z, elem.Position.y, elem.Position.w};
	m_Map.erase(vec);
	m_Queue.pop();
	ReleaseSRWLockShared(&m_Lock);
	return elem;
}

inline QueElement Updatequeue::top() {
	AcquireSRWLockShared(&m_Lock);
	QueElement elem = m_Queue.top();
	ReleaseSRWLockShared(&m_Lock);
	return elem;
}

Updatequeue::Updatequeue(IWorld * world) {
	m_World = world;
	m_Tick	= 0;
	InitializeSRWLock(&m_Lock);
}

Updatequeue::~Updatequeue() {
	save();
}

void Updatequeue::updateBlock(int x, int z, int y, byte type, UINT64 time) {
	if (time == 0) {
		INTVECTOR4 vec = INTVECTOR4(x, z, y, type);
		QueElement elem = {m_Tick + time, vec};
		workBlock(elem);
	} else
		enqueue(x, z, y, type, m_Tick + time);
}

void Updatequeue::updateBlock(QueElement elem) {
	if (time == 0) {
		workBlock(elem);
	} else {
		m_Queue.push(elem);
		int x = elem.Position.x;
		int y = elem.Position.y;
		int z = elem.Position.z;
		int type = elem.Position.w;
		UINT64 tick = elem.Time + m_Tick;
		QueElement e[] = {
			{tick,INTVECTOR4(x+1,z,y,type)}, {tick,INTVECTOR4(x-1,z,y,type)},
			{tick,INTVECTOR4(x,z+1,y,type)}, {tick,INTVECTOR4(x,z-1,y,type)},
			{tick,INTVECTOR4(x,z,y+1,type)}, {tick,INTVECTOR4(x,z,y-1,type)},

			{tick,INTVECTOR4(x,z,y,type)},
		};
		for ( int i = 0; i < 7; i++ ) {
			enqueue(e[i]);
		}
	}
}

bool Updatequeue::workBlock(QueElement elem) {
	int x			= elem.Position.x;
	int y			= elem.Position.y;
	int z			= elem.Position.z;
	byte type		= elem.Position.w;
	byte b			= 0;
	byte returnType = 0;
	
	if ((returnType = m_World->updateBlock(x, z, y, type, &b)) != 0) {
		if (returnType == 0xFF) return false;
		UINT64 update = b + m_Tick;		
		QueElement e[] = {
			{update,INTVECTOR4(x+1,z,y,returnType)}, {update,INTVECTOR4(x-1,z,y,returnType)},
			{update,INTVECTOR4(x,z+1,y,returnType)}, {update,INTVECTOR4(x,z-1,y,returnType)},
			{update,INTVECTOR4(x,z,y+1,returnType)}, {update,INTVECTOR4(x,z,y-1,returnType)},

			{update,INTVECTOR4(x,z,y,returnType)},
		};
		for ( int i = 0; i < 7; i++ ) {
			enqueue(e[i]);
		}
	}
	return true;
}

void Updatequeue::work() {
	m_Tick++;
	QueElement elem;

	while (!m_Queue.empty() && (m_Tick >= (elem = top()).Time)) {
		bool success = workBlock(elem);
		dequeue();
		if (!success) {
			elem.Time += 20;
			enqueue(elem);
		}
	}
}

void Updatequeue::save() {
	if (m_file) {
		DWORD size = static_cast<DWORD>(m_Queue.size());
		fseek(m_file, 0, SEEK_SET);
		fwrite(&size, sizeof(DWORD), 1, m_file);
		while (!m_Queue.empty()) {
			QueElement elem = dequeue();
			if (elem.Time >= m_Tick)
				elem.Time -= m_Tick;
			else
				elem.Time = 0;
			fwrite(&elem, sizeof(QueElement), 1, m_file);
		}
		fclose(m_file);
	}
}

void Updatequeue::load() {
	int file_exists;
	char filename[] = "Savegames/SaveQueue.que";

	fopen_s(&m_file, filename,"rb");
	if (m_file == NULL) 
		file_exists = 0;
	else {
		file_exists = 1; 
	}

	if (file_exists == 1)
	{
		printf("%s opened!\n", filename);
		if (m_file) {
			DWORD size = 0;
			fseek(m_file, 0, SEEK_END);
			long s = ftell(m_file);
			if (s) {
				fseek(m_file, 0, SEEK_SET);
				fread(&size, sizeof(DWORD), 1, m_file);
				while (size--) {
					QueElement elem;
					fread(&elem, sizeof(QueElement), 1, m_file);
					//printf("Read elem mith time %d\n", elem.Time);
					enqueue(elem);
				}
			}
			fclose(m_file);
			fopen_s(&m_file, filename,"w+b");
		}
	}
	else
	{
		printf("%s created!\n", filename);
		fopen_s(&m_file, filename,"w+b");
	}
}
